package net.ufrog.common.spring.exception;

import net.ufrog.common.exception.ServiceException;
import net.ufrog.common.spring.fastjson.FastJsonView;
import net.ufrog.common.utils.Objects;
import net.ufrog.common.utils.Strings;
import net.ufrog.common.web.RequestParam;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author ultrafrog, ufrog.net@gmail.com
 * @version 0.1, 2017-02-03
 * @since 0.1
 */
public class ExceptionResolver implements HandlerExceptionResolver {

    public static final String REQUEST_TYPE_HTML        = "html";
    public static final String REQUEST_TYPE_PART        = "part";
    public static final String REQUEST_TYPE_JSON        = "json";
    public static final String DEFAULT_PARAM_KEY        = "_request_type";
    public static final String DEFAULT_ERROR_VIEW       = "error";
    public static final String DEFAULT_PART_VIEW_SUFFIX = "_part";

    public static final String KEY_MODEL_EXCEPTION      = "_ex";

    /** 请求参数 */
    private String requestParamKey;

    /** 默认请求类型 */
    private String defaultRequestType;

    /** 错误视图 */
    private String errorView;

    /** 部分视图后缀 */
    private String partViewSuffix;

    /** 视图 */
    private View jsonView;

    /** 异常日志接口 */
    private ExceptionLogger exceptionLogger;

    /** 默认异常处理 */
    private ServiceExceptionHandler defaultExceptionHandler;

    /** 异常处理列表 */
    private List<ExceptionHandler> exceptionHandlers = new ArrayList<>();

    @Override
    @SuppressWarnings("unchecked")
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        RequestParam requestParam = RequestParam.current();
        String requestType = requestParam.getParams().containsKey(getRequestParamKey()) ? requestParam.getValue(getRequestParamKey()) : getDefaultRequestType();
        ModelAndView modelAndView = null;

        for (ExceptionHandler exceptionHandler: getExceptionHandlers()) {
            if (exceptionHandler.isType(e)) {
                modelAndView = exceptionHandler.handle(e, requestType, getErrorView(), getPartViewSuffix(), getJsonView(), getExceptionLogger());
                break;
            }
        }
        return checkModelAndView(modelAndView, e, requestType);
    }

    /**
     * 读取请求参数
     *
     * @return 请求参数
     */
    public String getRequestParamKey() {
        if (requestParamKey == null) {
            requestParamKey = DEFAULT_PARAM_KEY;
        }
        return requestParamKey;
    }

    /**
     * 设置请求参数
     *
     * @param requestParamKey 请求参数
     */
    public void setRequestParamKey(String requestParamKey) {
        this.requestParamKey = requestParamKey;
    }

    /**
     * 读取默认请求类型
     *
     * @return 默认请求类型
     */
    public String getDefaultRequestType() {
        if (defaultRequestType == null) {
            defaultRequestType = REQUEST_TYPE_HTML;
        }
        return defaultRequestType;
    }

    /**
     * 设置默认请求类型
     *
     * @param defaultRequestType 默认请求类型
     */
    public void setDefaultRequestType(String defaultRequestType) {
        this.defaultRequestType = defaultRequestType;
    }

    /**
     * 读取错误视图
     *
     * @return 错误视图
     */
    public String getErrorView() {
        if (errorView == null) {
            errorView = DEFAULT_ERROR_VIEW;
        }
        return errorView;
    }

    /**
     * 设置错误视图
     *
     * @param errorView 错误视图
     */
    public void setErrorView(String errorView) {
        this.errorView = errorView;
    }

    /**
     * 读取部分视图后缀
     *
     * @return 部分视图后缀
     */
    public String getPartViewSuffix() {
        if (partViewSuffix == null) {
            partViewSuffix = DEFAULT_PART_VIEW_SUFFIX;
        }
        return partViewSuffix;
    }

    /**
     * 设置部分视图后缀
     *
     * @param partViewSuffix 部分视图后缀
     */
    public void setPartViewSuffix(String partViewSuffix) {
        this.partViewSuffix = partViewSuffix;
    }

    /**
     * 读取视图
     *
     * @return 视图
     */
    public View getJsonView() {
        if (jsonView == null) {
            jsonView = new FastJsonView();
        }
        return jsonView;
    }

    /**
     * 设置视图
     *
     * @param jsonView 视图
     */
    public void setJsonView(View jsonView) {
        this.jsonView = jsonView;
    }

    /**
     * 读取异常日志接口
     *
     * @return 异常日志接口
     */
    public ExceptionLogger getExceptionLogger() {
        if (exceptionLogger == null) {
            exceptionLogger = new SimpleExceptionLogger();
        }
        return exceptionLogger;
    }

    /**
     * 设置异常日志接口
     *
     * @param exceptionLogger 异常日志接口
     */
    public void setExceptionLogger(ExceptionLogger exceptionLogger) {
        this.exceptionLogger = exceptionLogger;
    }

    /**
     * 读取异常处理列表
     *
     * @return 异常处理列表
     */
    public List<ExceptionHandler> getExceptionHandlers() {
        return exceptionHandlers;
    }

    /**
     * 检查对象<br>若为空则调用默认异常处理
     *
     * @param modelAndView model and view
     * @return model and view
     */
    private ModelAndView checkModelAndView(ModelAndView modelAndView, Exception e, String requestType) {
        if (modelAndView == null) {
            modelAndView = getDefaultExceptionHandler().handle(new ServiceException(e.getMessage(), e), requestType, getErrorView(), getPartViewSuffix(), getJsonView(), getExceptionLogger());
        }
        return modelAndView;
    }

    /**
     * @param requestType request type
     * @param errorView error view
     * @param partViewSuffix part view suffix
     * @param jsonView json view
     * @param models model array
     * @return model and view
     */
    public static ModelAndView modelAndView(String requestType, String errorView, String partViewSuffix, View jsonView, Object... models) {
        if (Strings.equals(ExceptionResolver.REQUEST_TYPE_JSON, requestType)) {
            return new ModelAndView(jsonView, Objects.map(models));
        } else {
            return new ModelAndView(errorView + (Strings.equals(ExceptionResolver.REQUEST_TYPE_PART, requestType) ? partViewSuffix : ""), Objects.map(models));
        }
    }

    /**
     * 读取默认异常处理
     *
     * @return 默认异常处理
     */
    private ServiceExceptionHandler getDefaultExceptionHandler() {
        if (defaultExceptionHandler == null) {
            Optional<ExceptionHandler> oExceptionHandler = exceptionHandlers.stream().filter(exceptionHandler -> exceptionHandler.getClass() == ServiceExceptionHandler.class).findFirst();
            defaultExceptionHandler = (ServiceExceptionHandler) oExceptionHandler.orElseGet(ServiceExceptionHandler::new);
        }
        return defaultExceptionHandler;
    }
}
